package com.xtayfjpk.security.jaas.demo;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.Policy;
import java.security.Principal;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;

import sun.misc.JavaSecurityProtectionDomainAccess;
import sun.misc.JavaSecurityProtectionDomainAccess.ProtectionDomainCache;
import sun.security.provider.PolicyFile;

public class DemoLoginModule implements LoginModule {
	private Subject subject;
	private CallbackHandler callbackHandler;
	private boolean success = false;
	private String user;
	private String password;

	@Override
	public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
		this.subject = subject;
		this.callbackHandler = callbackHandler;
	}

	@Override
	public boolean login() throws LoginException {
		NameCallback nameCallback = new NameCallback("请输入用户名");
		PasswordCallback passwordCallback = new PasswordCallback("请输入密码", false);
		Callback[] callbacks = new Callback[]{nameCallback, passwordCallback};
		try {
			callbackHandler.handle(callbacks);
			user = nameCallback.getName();
			password = new String(passwordCallback.getPassword());
		} catch (IOException | UnsupportedCallbackException e) {
			success = false;
			throw new FailedLoginException("用户名或密码获取失败");
		}
		//为简单起见校验写死
		if(user.length()>3 && password.length()>3) {
			success = true;
		}
		return true;
	}

	@Override
	public boolean commit() throws LoginException {
		if(!success) {
			return false;
		} else {
			System.out.println(user);
			Principal principal = new DemoPrincipal(user);
			this.subject.getPrincipals().add(principal);
			/*try {
				Method checkMethod = Policy.class.getDeclaredMethod("getPolicyNoCheck");
				checkMethod.setAccessible(true);
				Object obj = checkMethod.invoke(null);
				PolicyFile systemPolicy = (PolicyFile) obj;
				
				Field policyInfoField = PolicyFile.class.getDeclaredField("policyInfo");
				policyInfoField.setAccessible(true);
				obj = policyInfoField.get(systemPolicy);
				AtomicReference policyInfoRef = (AtomicReference) obj;
				Object policyInfo = policyInfoRef.get();
				Method mappingMethod = policyInfo.getClass().getDeclaredMethod("getPdMapping");
				mappingMethod.setAccessible(true);
				JavaSecurityProtectionDomainAccess.ProtectionDomainCache cache = (ProtectionDomainCache) mappingMethod.invoke(policyInfo);
				
				
				AccessControlContext context = AccessController.getContext();
				Field contextField = context.getClass().getDeclaredField("context");
				contextField.setAccessible(true);
				ProtectionDomain[] domains = (ProtectionDomain[]) contextField.get(context);
				if(domains!=null) {
					for(ProtectionDomain domain : domains) {
						Field principalsField = ProtectionDomain.class.getDeclaredField("principals");
						principalsField.setAccessible(true);
						Principal[] principals = (Principal[]) principalsField.get(domain);
						List<Principal> list = new ArrayList<>(Arrays.asList(principals));
						list.add(principal);
						principalsField.set(domain, list.toArray(new Principal[0]));
						cache.put(domain, null);
					}
				}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}*/
			return true;
		}
	}

	@Override
	public boolean abort() throws LoginException {
		logout();
		return true;
	}

	@Override
	public boolean logout() throws LoginException {
		System.out.println("logout");
		Iterator<Principal> iter = subject.getPrincipals().iterator();
		while(iter.hasNext()) {
			Principal principal = iter.next();
			if(principal instanceof DemoPrincipal) {
				if(principal.getName().equals(user)) {
					iter.remove();
					break;
				}
			}
		}
		return true;
	}

}
